package com.usermanagement.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cac409club.usermanagement.dto.*;
import com.usermanagement.dto.AssignPermissionDTO;
import com.usermanagement.dto.LoginDTO;
import com.usermanagement.dto.ResetPasswordDTO;
import com.usermanagement.dto.UserDTO;
import com.usermanagement.entity.UserDO;
import com.usermanagement.exception.BusinessException;
import com.usermanagement.mapper.UserMapper;
import com.usermanagement.service.impl.UserServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

public class UserServiceImplTest {

    @Mock
    private UserMapper userMapper;

    @InjectMocks
    private UserServiceImpl userService;

    @BeforeEach
    public void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void testRegisterUser_Success() {
        UserDTO userDTO = new UserDTO();
        userDTO.setUsername("testuser");
        userDTO.setPasswordHash("hashedpassword");
        userDTO.setEmail("test@example.com");

        when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(null);
        when(userMapper.insert(any(UserDO.class))).thenReturn(1);

        assertTrue(userService.registerUser(userDTO));

        verify(userMapper).selectOne(any(QueryWrapper.class));
        verify(userMapper).insert(any(UserDO.class));
    }

    @Test
    public void testRegisterUser_Failure_ExistingUsername() {
        UserDTO userDTO = new UserDTO();
        userDTO.setUsername("testuser");
        userDTO.setPasswordHash("hashedpassword");
        userDTO.setEmail("test@example.com");

        UserDO existingUser = new UserDO();
        existingUser.setUsername("testuser");

        when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(existingUser);

        assertThrows(BusinessException.class, () -> userService.registerUser(userDTO));

        verify(userMapper).selectOne(any(QueryWrapper.class));
        verify(userMapper, never()).insert(any(UserDO.class));
    }

    @Test
    public void testLoginUser_Success() {
        LoginDTO loginDTO = new LoginDTO();
        loginDTO.setUsernameOrEmail("testuser");
        loginDTO.setPasswordHash("hashedpassword");

        UserDO userDO = new UserDO();
        userDO.setUsername("testuser");
        userDO.setPasswordHash("hashedpassword");

        when(userMapper.selectByUsernameOrEmail(anyString(), anyString())).thenReturn(userDO);

        UserDO result = userService.loginUser(loginDTO);

        assertNotNull(result);
        assertEquals("testuser", result.getUsername());

        verify(userMapper).selectByUsernameOrEmail(anyString(), anyString());
    }

    @Test
    public void testLoginUser_Failure_UserNotFound() {
        LoginDTO loginDTO = new LoginDTO();
        loginDTO.setUsernameOrEmail("nonexistentuser");
        loginDTO.setPasswordHash("hashedpassword");

        when(userMapper.selectByUsernameOrEmail(anyString(), anyString())).thenReturn(null);

        assertThrows(BusinessException.class, () -> userService.loginUser(loginDTO));

        verify(userMapper).selectByUsernameOrEmail(anyString(), anyString());
    }

    @Test
    public void testLoginUser_Failure_PasswordMismatch() {
        LoginDTO loginDTO = new LoginDTO();
        loginDTO.setUsernameOrEmail("testuser");
        loginDTO.setPasswordHash("wronghashedpassword");

        UserDO userDO = new UserDO();
        userDO.setUsername("testuser");
        userDO.setPasswordHash("hashedpassword");

        when(userMapper.selectByUsernameOrEmail(anyString(), anyString())).thenReturn(userDO);

        assertThrows(BusinessException.class, () -> userService.loginUser(loginDTO));

        verify(userMapper).selectByUsernameOrEmail(anyString(), anyString());
    }

    @Test
    public void testResetPassword_Success() {
        ResetPasswordDTO resetPasswordDTO = new ResetPasswordDTO();
        resetPasswordDTO.setUserId(1);
        resetPasswordDTO.setNewPasswordHash("newhashedpassword");

        UserDO userDO = new UserDO();
        userDO.setUserId(1);
        userDO.setPasswordHash("oldhashedpassword");

        when(userMapper.selectByUserId(anyInt())).thenReturn(userDO);
        when(userMapper.updateById(any(UserDO.class))).thenReturn(1);

        assertTrue(userService.resetPassword(resetPasswordDTO));

        verify(userMapper).selectByUserId(anyInt());
        verify(userMapper).updateById(any(UserDO.class));
    }

    @Test
    public void testResetPassword_Failure_UserNotFound() {
        ResetPasswordDTO resetPasswordDTO = new ResetPasswordDTO();
        resetPasswordDTO.setUserId(1);
        resetPasswordDTO.setNewPasswordHash("newhashedpassword");

        when(userMapper.selectByUserId(anyInt())).thenReturn(null);

        assertThrows(BusinessException.class, () -> userService.resetPassword(resetPasswordDTO));

        verify(userMapper).selectByUserId(anyInt());
        verify(userMapper, never()).updateById(any(UserDO.class));
    }

    @Test
    public void testAssignPermissions_Success() {
        AssignPermissionDTO assignPermissionDTO = new AssignPermissionDTO();
        assignPermissionDTO.setUserId(1);
        assignPermissionDTO.setRoleId(2);
        assignPermissionDTO.setIdentityId(3);

        UserDO userDO = new UserDO();
        userDO.setUserId(1);

        when(userMapper.selectByUserId(anyInt())).thenReturn(userDO);
        when(userMapper.countRoleById(anyInt())).thenReturn(1);
        when(userMapper.countIdentityById(anyInt())).thenReturn(1);
        when(userMapper.updateById(any(UserDO.class))).thenReturn(1);

        assertTrue(userService.assignPermissions(assignPermissionDTO));

        verify(userMapper).selectByUserId(anyInt());
        verify(userMapper).countRoleById(anyInt());
        verify(userMapper).countIdentityById(anyInt());
        verify(userMapper).updateById(any(UserDO.class));
    }

    @Test
    public void testAssignPermissions_Failure_UserNotFound() {
        AssignPermissionDTO assignPermissionDTO = new AssignPermissionDTO();
        assignPermissionDTO.setUserId(1);
        assignPermissionDTO.setRoleId(2);
        assignPermissionDTO.setIdentityId(3);

        when(userMapper.selectByUserId(anyInt())).thenReturn(null);

        assertThrows(BusinessException.class, () -> userService.assignPermissions(assignPermissionDTO));

        verify(userMapper).selectByUserId(anyInt());
        verify(userMapper, never()).countRoleById(anyInt());
        verify(userMapper, never()).countIdentityById(anyInt());
        verify(userMapper, never()).updateById(any(UserDO.class));
    }

    @Test
    public void testAssignPermissions_Failure_RoleOrIdentityNotFound() {
        AssignPermissionDTO assignPermissionDTO = new AssignPermissionDTO();
        assignPermissionDTO.setUserId(1);
        assignPermissionDTO.setRoleId(2);
        assignPermissionDTO.setIdentityId(3);

        UserDO userDO = new UserDO();
        userDO.setUserId(1);

        when(userMapper.selectByUserId(anyInt())).thenReturn(userDO);
        when(userMapper.countRoleById(anyInt())).thenReturn(0);

        assertThrows(BusinessException.class, () -> userService.assignPermissions(assignPermissionDTO));

        verify(userMapper).selectByUserId(anyInt());
        verify(userMapper).countRoleById(anyInt());
        verify(userMapper, never()).countIdentityById(anyInt());
        verify(userMapper, never()).updateById(any(UserDO.class));
    }
}